package org.hepeng.workx.spring.cloud.netflix.eureka.client;

import com.netflix.appinfo.ApplicationInfoManager;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.SystemUtils;
import org.hepeng.workx.monitor.linux.LoadAverageMonitor;
import org.hepeng.workx.util.SigarUtils;
import org.hepeng.workx.util.concurrent.NamedThreadFactory;
import org.hyperic.sigar.CpuPerc;
import org.hyperic.sigar.Mem;
import org.hyperic.sigar.OperatingSystem;
import org.hyperic.sigar.Sigar;
import org.hyperic.sigar.Swap;
import org.joor.Reflect;
import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaRegistration;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author he peng
 */

@Slf4j
public class EurekaInstanceMachineRefreshTask {

    private static final Sigar SIGAR = SigarUtils.getSigar();

    private EurekaRegistration registration;
    private EurekaHttpClient eurekaHttpClient;

    public EurekaInstanceMachineRefreshTask(EurekaRegistration registration) {
        this.registration = registration;
        Object eurekaTransport = Reflect.on(registration.getEurekaClient()).get("eurekaTransport");
        this.eurekaHttpClient = Reflect.on(eurekaTransport).get("registrationClient");

        ScheduledThreadPoolExecutor threadPoolExecutor = new ScheduledThreadPoolExecutor(2
                , new NamedThreadFactory("EurekaInstanceMachineStateRefresh" , true));
        threadPoolExecutor.scheduleAtFixedRate(
                new EurekaInstanceMachineRefreshThread() , 5 , 3 , TimeUnit.SECONDS);
    }


    private void refreshMachineState() throws Exception {
        log.debug("eureka instance Refresh Machine State");
        ApplicationInfoManager applicationInfoManager = registration.getApplicationInfoManager();
        InstanceInfo instanceInfo = applicationInfoManager.getInfo();
        cpu(applicationInfoManager);
        mem(applicationInfoManager);
        os(applicationInfoManager);

        eurekaHttpClient.register(instanceInfo);
    }

    private void cpu(ApplicationInfoManager appInfoManager) throws Exception {
        Map<String, String> metadata = new HashMap<>();
        int cpus = SIGAR.getCpuList().length;
        metadata.put("cpus" , String.valueOf(cpus));

        double combined = 0.0 , sys = 0.0 , user = 0.0 , idle = 0.0;
        for (CpuPerc cpuInfo : SIGAR.getCpuPercList()) {
            combined += cpuInfo.getCombined();
            sys += cpuInfo.getSys();
            user += cpuInfo.getUser();
            idle += cpuInfo.getIdle();
        }
        metadata.put("cpu.combined.time" , CpuPerc.format(combined / cpus));
        metadata.put("cpu.sys.time" , CpuPerc.format(sys / cpus));
        metadata.put("cpu.user.time" , CpuPerc.format(user / cpus));
        metadata.put("cpu.idle.time" , CpuPerc.format(idle / cpus));

        if (SystemUtils.IS_OS_LINUX) {
            LoadAverageMonitor.LinuxLoadAverage loadAverage = LoadAverageMonitor.getLoadAverage();
            metadata.put("load.average.1m" , String.valueOf(loadAverage.getLoadAverageWithin1m()));
            metadata.put("load.average.5m" , String.valueOf(loadAverage.getLoadAverageWithin5m()));
            metadata.put("load.average.15m" , String.valueOf(loadAverage.getLoadAverageWithin15m()));
            metadata.put("load.average.pre.cpu.1m" , String.valueOf(loadAverage.getLoadAverageWithin1m() / cpus));
            metadata.put("load.average.pre.cpu.5m" , String.valueOf(loadAverage.getLoadAverageWithin5m() / cpus));
            metadata.put("load.average.pre.cpu.15m" , String.valueOf(loadAverage.getLoadAverageWithin15m() / cpus));
        }

        appInfoManager.registerAppMetadata(metadata);
    }

    private void mem(ApplicationInfoManager appInfoManager) throws Exception {
        Map<String, String> metadata = new HashMap<>();
        Mem mem = SIGAR.getMem();
        metadata.put("mem.total" , String.valueOf(mem.getTotal()));
        metadata.put("mem.used" , String.valueOf(mem.getUsed()));
        metadata.put("mem.free" , String.valueOf(mem.getFree()));

        Swap swap = SIGAR.getSwap();
        metadata.put("mem.swap.total" , String.valueOf(swap.getTotal()));
        metadata.put("mem.swap.used" , String.valueOf(swap.getUsed()));
        metadata.put("mem.swap.free" , String.valueOf(swap.getFree()));

        appInfoManager.registerAppMetadata(metadata);
    }

    private void os(ApplicationInfoManager appInfoManager) {
        Map<String, String> metadata = new HashMap<>();
        OperatingSystem os = OperatingSystem.getInstance();
        metadata.put("os.arch" , os.getArch());
        metadata.put("os.name" , os.getName());
        metadata.put("os.version" , os.getVersion());
        metadata.put("os.vendor" , os.getVendor());
        metadata.put("os.vendor.name" , os.getVendorName());
        metadata.put("os.vendor.version" , os.getVersion());
        appInfoManager.registerAppMetadata(metadata);
    }


    private class EurekaInstanceMachineRefreshThread implements Runnable {
        @Override
        public void run() {
            try {
                refreshMachineState();
            } catch (Exception e) {
                log.error("eureka instance Refresh Machine State Error {} " , e);
            }
        }
    }
}
